package ua.edu.nuos.jeetraining2012.cms.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ua.edu.nuos.jeetraining2012.cms.dto.HomeWorkDTO;
import ua.edu.nuos.jeetraining2012.cms.dto.HomeWorkDetailedDTO;
import ua.edu.nuos.jeetraining2012.cms.dto.UpcomingHomeWorkDTO;
import ua.edu.nuos.jeetraining2012.cms.entity.*;
import ua.edu.nuos.jeetraining2012.cms.model.HomeWorkDAO;
import ua.edu.nuos.jeetraining2012.cms.model.StudentsToHomeWorkDAO;
import ua.edu.nuos.jeetraining2012.cms.model.exception.DAOException;
import ua.edu.nuos.jeetraining2012.cms.service.exception.ServiceException;
import ua.edu.nuos.jeetraining2012.cms.service.exception.ServiceValidationException;

import javax.annotation.Resource;
import javax.ejb.EJB;
import javax.ejb.Local;
import javax.ejb.Stateless;
import javax.validation.ConstraintViolation;
import javax.validation.ValidatorFactory;
import java.util.*;

/**
 * Define Homework logic.
 * <p/>
 *
 * @author Victor N. Ganichev victor.ganichev@gmail.com
 * @version 1.0 Date: 24.06.13 Time: 11:25
 * @since 1.0
 */
@Local
@Stateless
public class HomeWork {

    /** Define DAO Layer for class. */
    @EJB
    private HomeWorkDAO dao;

    /** Define DAO layer for student progress. */
    @EJB
    private StudentsToHomeWorkDAO studentsToHomeWorkDAO;

    /**
     * Bean validation factory.
     */
    @SuppressWarnings({"EjbEnvironmentInspection"})
    @Resource
    private ValidatorFactory validatorFactory;

    /** Define logger */
    final private static Logger logger = LoggerFactory.getLogger(HomeWork.class);

    // Constructors
    public HomeWork() {
        logger.debug("HomeWork invocation.");
    }

    /**
     * Get all upcoming students Home work.
     * <p/>
     * Return list of upcoming HomeWork for student, by iterating throw list of enrolled courses withing
     * <code>courseList</code>.<br/>
     * If courseList is <code>null</code> or empty, IllegalArgumentException will be thrown.
     *
     * @param courseList                    Students enrolled course list.
     * @return                              List of upcoming HomeWorks;
     * @throws IllegalArgumentException     If courseList is null or empty.
     */
    public List<UpcomingHomeWorkDTO> getAllUpcomingStudentHw(List<CourseEntity> courseList) throws IllegalArgumentException {
        logger.debug("getAllUpcomingStudentHw() - started.");

        if (courseList == null || courseList.isEmpty()) {
            logger.error("Course list can't be {}", courseList);
            logger.debug("getAllUpcomingStudentHw() - finished with IllegalArgumentException.");
            throw new IllegalArgumentException("Course list can't be null or empty.");
        }

        logger.debug("Trying to compose list of upcoming HW for course with ID.");
        List<UpcomingHomeWorkDTO> hwList = new ArrayList<>();
        for (CourseEntity course : courseList) {
            try {
                UpcomingHomeWorkDTO hw = dao.findNearestByCourseId(course.getId());
                if (hw != null) {
                    hwList.add(hw);
                }
                logger.info("Found {} records", hwList.size());
            } catch (DAOException e) {
                logger.error("Can't fetch HW. Got DAOException with error message {} and \nTrace\n {}", e.getMessage(),
                        e.getStackTrace());
                logger.debug("Returning empty list.");
            }
        }
        logger.debug("Size of list is {}", hwList.size());
        logger.debug("getAllUpcomingStudentHw() - finished.");
        return hwList;
    }

    /**
     * Return List of short homework data {@link HomeWorkDTO} by course Id or <code>null</code>.
     * <p/>
     * If course ID is <code>null</code> or less than 1, IllegalArgumentException will be thrown.
     *
     * @param courseId                      Course Id.
     * @return                              List of HomeWorkDTO.
     * @throws IllegalArgumentException     If course ID is null or less than 1.
     */
    public List<HomeWorkDTO> getAllHomeworkByCourse(Long courseId) throws IllegalArgumentException {
        logger.debug("getAllHomeworkByCourse() - started.");

        if (courseId == null || courseId < 1) {
            logger.error("Course ID can't be {}", courseId);
            logger.debug("getAllHomeworkByCourse() - finished with IllegalArgumentException.");
            throw new IllegalArgumentException("Course ID can't be null or less than 1.");
        }

        logger.debug("Trying to get all HomeWork for this course.");
        List<HomeWorkDTO> hwList = null;
        try {
            hwList = dao.findHomeWorkListByCourseId(courseId);
        } catch (DAOException e) {
            logger.error("Can't find any Home work, got DAOException with message {} and \ntrace:\n {}",
                    e.getMessage(), e.getStackTrace());
        }
        if (hwList != null) {
            logger.debug("Return {} homework items.", hwList.size());
        } else {
            logger.debug("Return null.");
        }
        logger.debug("getAllHomeworkByCourse() - finished.");
        return hwList;
    }

    /**
     * Find all homework, available for material with id <code>materialId</code>.
     * <p/>
     * In case of success, return <code>List&lt;HomeWorkEntity&gt;</code>.
     * <p/>
     * If materialId is <code>null</code> or less than 1, IllegalArgumentException will be thrown.<br/>
     * If any parent exception appeared, {@link ua.edu.nuos.jeetraining2012.cms.model.exception.DAOException} will be thrown.<br/>
     *
     * @param materialId                    Material ID.
     * @return                              List of homework, available for this material.
     * @throws IllegalArgumentException     If materialId is null or less than 1.
     */
    public List<HomeWorkEntity> getHomeworkByMaterialId(Long materialId) throws IllegalArgumentException {
        logger.debug("getHomeworkByMaterialId() - started.");

        if (materialId == null || materialId < 1) {
            logger.error("Course ID can't be {}", materialId);
            logger.debug("getHomeworkByMaterialId() - finished with IllegalArgumentException.");
            throw new IllegalArgumentException("Material ID can't be null or less than 1.");
        }

        List<HomeWorkEntity> homework = null;
        logger.debug("Trying to fetch HW for course with ID {}", materialId);
        try {
            homework = dao.findHomeworkByMaterialId(materialId);
            if (homework != null) {
                logger.info("Found {} HW", homework.size());
            }
        } catch (DAOException e) {
            logger.error("Can't find HW because of DAOException. Error message: {}.\nTrace:\n {}", e.getMessage(),
                    e.getStackTrace());
            logger.debug("Returning empty list");
            homework = new ArrayList<>();
        }
        logger.debug("getHomeworkByMaterialId() - finished.");
        return homework;
    }

    /**
     * Get detailed information about home work.
     * <p/>
     * In case of success, instance of {@link HomeWorkDetailedDTO} will be returned, either <code>null</code>.
     * <p/>
     * If homeWorkId is <code>null</code> or less than 1, IllegalArgumentException will be thrown.
     *
     * @param homeWorkId                    Homework ID.
     * @return                              HomeWorkDetailedDTO
     * @throws IllegalArgumentException     If homeWorkId is null or less than 1.
     */
    public HomeWorkDetailedDTO getHomeWork(Long homeWorkId) throws IllegalArgumentException {
        logger.debug("getStudentHomeWork() - started.");

        if (homeWorkId == null || homeWorkId < 1) {
            logger.error("Home work ID can't be {}", homeWorkId);
            logger.debug("getStudentHomeWork() - finished with IllegalArgumentException.");
            throw new IllegalArgumentException("Home Work ID can't be null or less than 1.");
        }

        HomeWorkDetailedDTO dto = null;
        try {
            dto = dao.findHomeworkById(homeWorkId);
        } catch (DAOException e) {
            logger.error("Can't find, got DAOException. Message {} \nTrace:\n {}", e.getMessage(),
                    e.getStackTrace());
            logger.debug("Null will be returned.");
        }

        logger.debug("getStudentHomeWork() - finished.");
        return dto;
    }


    public StudentsToHomeWorkEntity getStudentProgress(String userId, Long homeworkId) throws IllegalArgumentException {
        logger.debug("getStudentProgress() - started.");

        if (userId == null || userId.isEmpty()) {
            logger.error("User ID can't be {}", userId);
            logger.debug("getStudentProgress() - finished with IllegalArgumentException.");
            throw new IllegalArgumentException("User ID can't be null or empty.");
        }

        if (homeworkId == null || homeworkId < 1) {
            logger.error("Homework ID can't be {}", homeworkId);
            logger.debug("getStudentProgress() - finished with IllegalArgumentException.");
            throw new IllegalArgumentException("Homework ID can't be null or less than 1.");
        }

        StudentsToHomeWorkId id = new StudentsToHomeWorkId(userId, homeworkId);
        StudentsToHomeWorkEntity entity = null;
        try {
             entity = studentsToHomeWorkDAO.findStudentProgressById(id);
        } catch (DAOException e) {
            logger.error("Got DAOException with message {} and \ntrace\n {}", e.getMessage(), e.getStackTrace());
            logger.debug("getStudentProgress() - finished with DAOException, null result will be returned.");
        }

        logger.debug("getStudentProgress() - finished.");
        return entity;
    }


    /**
     * Persist homework in storage. If this is new homework entry it will be added, otherwise existed homework entry is updated
     *
     * @param homework HomeWorkEntity to be persisted
     * @throws ua.edu.nuos.jeetraining2012.cms.service.exception.ServiceValidationException
     *                                  When HomeWorkEntity is invalid
     * @throws IllegalArgumentException When HomeWorkEntity is null
     * @throws ua.edu.nuos.jeetraining2012.cms.service.exception.ServiceException         When underlying storage throw an exception
     */
    public void persist(HomeWorkEntity homework) throws ServiceValidationException, ServiceException {
        logger.debug("persist() - started.");
        if (homework == null)
            throw new IllegalArgumentException("DTO can't be null");

        Set<ConstraintViolation<HomeWorkEntity>> validationResult = validatorFactory.getValidator().validate(homework);
        if (!validationResult.isEmpty()) {
            throw new ServiceValidationException(validationResult);
        }
        boolean exist = isExist(homework);
        try {
            if (exist) {
                dao.update(homework);
            } else {
                dao.create(homework);
            }
        } catch (DAOException e) {
            logger.error("persist() - Received DAO Exception", e);
            throw new ServiceException("persist() - Received DAO Exception: " + e.getMessage());
        }
    }


    /**
     * Check record for existence in DB.
     * If record presents in DB, True will be returned, either false.
     * If entity is null, IllegalArgumentException will be thrown.
     *
     * @param entity                        HomeWorkEntity object.
     * @return                              True if record presents in DB.
     * @throws IllegalArgumentException     If homework in null.
     */
    public boolean isExist(HomeWorkEntity entity) throws IllegalArgumentException {
        logger.debug("isExist() - started.");
        if (entity == null) {
            logger.error("isNew() HomeWork entity can't be {}", entity);
            logger.debug("isNew() finished with IllegalArgumentException.");
            throw new IllegalArgumentException("HomeWork entity can't be null");
        }
        boolean isExists = ((entity.getId() != 0) && getHomeWork(entity.getId()) != null);

        logger.debug("isExist() Record is {} in DB", ((isExists)? "exists": "not exists"));
        logger.debug("isNew() - finished.");
        return isExists;
    }

    /**
     * Constructs and return valid empty entity object
     *
     * @return Empty valid CourseMaterialEntity object
     */
    public HomeWorkEntity getEmptyEntity() {
        HomeWorkEntity entity = new HomeWorkEntity();
        String str = new String();
        entity.setId(0);
        entity.setTitle(str);
        entity.setStartDate(new Date());
        entity.setDueDate(new Date());
        entity.setDescription(str);
        return entity;
    }

    /**
     * Remove record from DB.
     * In case of success, True will be returned.
     *
     * If course is null, IllegalArgumentException will be thrown.
     * If parent DAOException appeared, ServiceException will be thrown.
     *
     * @param entity                        HomeWorkEntity object.
     * @return                              True if record success removed.
     * @throws IllegalArgumentException     If course is null.
     * @throws ServiceException             If parent DAOException appeared.
     */
    public boolean delete(HomeWorkEntity entity) throws IllegalArgumentException, ServiceException {
        logger.debug("delete() - started.");

        if (entity == null) {
            logger.error("Course entity can't be {}", entity);
            logger.debug("delete() - finished with IllegalArgumentException.");
            throw new IllegalArgumentException("Course entity can't be null");
        }

        if (!isExist(entity)) {
            logger.debug("delete() - finished. Record not exists in DB.");
            return false;
        }

        logger.debug("delete() Removing course: {} from DB", entity);
        boolean isDeleted = false;
        try {
            isDeleted = dao.delete(entity);
            logger.info("delete() Record removed from DB.");
            logger.debug("delete() - finished.");
            return isDeleted;
        } catch (DAOException e) {
            logger.error("delete() Can't remove record from DB, got DAOException with error: {} with \nTrace:\n {}",
                    e.getMessage(), Arrays.deepToString(e.getStackTrace()));
            logger.debug("delete() - finished with ServiceException.");
            throw new ServiceException("Can't remove course from DB. Error message: " + e.getMessage());
        }
    }

}
